package raft

import (
	`math/rand`
	`sync`
	`sync/atomic`
	`time`
)

var randSeed int64 = 1

func NeverPanic() {
	if p := recover(); p != nil { // 如果发生异常，就。。。
		DPrintf("never panic get error: %v", p)
	}
}

func Now() time.Duration {
	return time.Duration(time.Now().UnixNano())
}

// RandTime(n) -> rand[0, n)
// RandTime(m, n) -> rand[m, n)
func RandTime(n time.Duration, line ...time.Duration) time.Duration {
	randSeed++
	rand.Seed(randSeed)
	if len(line) == 0 {
		return time.Duration(rand.Int63n(int64(n)))
	}
	m, nn := int64(n), int64(line[0])
	return time.Duration(nn - rand.Int63n(nn-m))
}

func Min(a, b int) int {
	if a > b {
		return b
	}
	return a
}

type EventHook struct {
	mu               sync.Mutex
	listenChangeLine []chan<- struct{}
}

func (eh *EventHook) Happen() {
	eh.mu.Lock()
	defer eh.mu.Unlock()
	//DPrintf("[%d] change identity, now: %d, next: %d", rf.me, rf.identity, identity)
	if len(eh.listenChangeLine) == 0 {
		return
	}

	chanLine := make([]chan<- struct{}, len(eh.listenChangeLine))
	copy(chanLine, eh.listenChangeLine)
	eh.listenChangeLine = []chan<- struct{}{}

	for _, c := range chanLine {
		go func(c chan<- struct{}) {
			defer NeverPanic()
			c <- struct{}{}
		}(c)
	}
}

func (eh *EventHook) Listen(ch chan<- struct{}) {
	eh.mu.Lock()
	defer eh.mu.Unlock()
	eh.listenChangeLine = append(eh.listenChangeLine, ch)
}

type HeartBeatHelper struct {
	nextTimeOut int64
}

// 将nextTimeOut更新到now + HeartBeatTimeOut，返回这个时间
func (tm *HeartBeatHelper) updateTimeOut() (t time.Duration) {
	t = Now() + HeartbeatTimeout + RandTime(HeartbeatTimeout)
	atomic.StoreInt64(&tm.nextTimeOut, int64(t))
	return
}

// 将nextTimeOut更新到now + HeartBeatTimeOut，返回这个时间
func (tm *HeartBeatHelper) getNextTimeOut() time.Duration {
	return time.Duration(atomic.LoadInt64(&tm.nextTimeOut))
}

func (tm *HeartBeatHelper) SleepUntilNextTimeOut() {
	time.Sleep(tm.getNextTimeOut() - Now())
}
